package com.jkl.nettyserver.Netty;

import com.jkl.nettyserver.Tool.Zookeeper.ZookeeperClient;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Component
@Slf4j
public class WebSocketHandler extends ChannelInboundHandlerAdapter {
    //当Channel被注册到EventLoop时调用。
    final ZookeeperClient zookeeperClient;
    private static final Pattern QUERY_PARAM_PATTERN = Pattern.compile("([^&]+)=([^&]*)");
    private String email;
    @Autowired
    public WebSocketHandler(ZookeeperClient zookeeperClient) {
        this.zookeeperClient = zookeeperClient;
    }
    //用户连接时调用
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        log.info("[{}] registered.",ctx.channel().id() );
        zookeeperClient.addConnectCount();
        super.channelRegistered(ctx);
    }
    //用户断开连接时调用
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        if(email!=null){
            if(!General.LOCK_SET.contains(email)){
                General.CHANNEL_MAP.remove(email);//移除 账号-管道
            }
            else{
                General.LOCK_SET.remove(email);
            }
        }
        zookeeperClient.reduceConnectCount();
        log.info("[{}] Unregistered.",ctx.channel().id());
        super.channelUnregistered(ctx);
    }

    //当WebSocket握手成功时，Netty会触发一个HandshakeComplete事件，该方法就是用来处理这个事件的。
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws InterruptedException {
        if(evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) {
            WebSocketServerProtocolHandler.HandshakeComplete complete=(WebSocketServerProtocolHandler.HandshakeComplete)evt;
            Map<String, String> queryParams = parseQueryParams(complete.requestUri());
            email=queryParams.get("email");
            String lastTime=queryParams.get("lastTime");
            if(email!=null&&lastTime!=null)
            {
                handleExistingChannel(ctx,email);
                log.info("[{} : {}] joined.",email,ctx.channel().id());
            }
            else{
                closeContext(ctx,MsgDegree.WARN,"Missing email or lastTime parameter");
            }
        }
    }
    private Map<String, String> parseQueryParams(String uri) {
        Map<String, String> queryParams = new HashMap<>();
        int queryIndex = uri.indexOf('?');
        if (queryIndex != -1) {
            Matcher matcher = QUERY_PARAM_PATTERN.matcher(uri.substring(queryIndex + 1));
            while (matcher.find()) {
                queryParams.put(matcher.group(1), matcher.group(2));
            }
        }
        return queryParams;
    }

    private void handleExistingChannel(ChannelHandlerContext ctx,String email) throws InterruptedException {
        System.out.println(email);
        System.out.println(General.CHANNEL_MAP.containsKey(email));
        if(General.CHANNEL_MAP.containsKey(email)){
            Channel channel = General.CHANNEL_MAP.get(email);
            General.LOCK_SET.add(email);//给管道加锁,防止Channel在调用put()前,后调用remove();
            channel.close().sync();
        }
        General.CHANNEL_MAP.put(email,ctx.channel());//保存 账号-管道
    }

    //异常触发
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause){
        closeContext(ctx, MsgDegree.ERROR,cause.getMessage());
    }
    private void closeContext(ChannelHandlerContext ctx,MsgDegree degree, String reason) {
        switch (degree){
            case DEBUG: log.debug("Closing context due to: {}", reason);
            case INFO: log.info("Closing context due to: {}", reason);break;
            case WARN: log.warn("Closing context due to: {}", reason);break;
            case ERROR: log.error("Closing context due to: {}", reason);break;
            default: System.out.println("Closing context due to: "+reason);
        }
        ctx.close();
    }
}
